package plus.easydo.starter.oauth.resources.service;


import org.springframework.security.access.expression.SecurityExpressionRoot;
import org.springframework.security.access.expression.method.MethodSecurityExpressionOperations;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import plus.easydo.starter.oauth.core.model.CustomizeUsernamePasswordAuthenticationToken;

import java.io.Serializable;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 自定义权限校验表达式
 *
 * @author yuzhanfeng
 */
public class MySecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {

    private static final String ALL = "ALL";

    private static final String ALL_PERMISSION = "*:*:*";

    private Object filterObject;

    private Object returnObject;

    private Object target;

    private Collection<? extends Serializable> permissions;

    private Object user;

    private OAuth2Request oAuth2Request;

    /**
     * 构造器
     *
     * @param authentication authentication
     */
    public MySecurityExpressionRoot(Authentication authentication) {
        super(authentication);
        if (authentication instanceof OAuth2Authentication) {
            OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) authentication;
            Authentication userAuthentication = oAuth2Authentication.getUserAuthentication();
            this.oAuth2Request = oAuth2Authentication.getOAuth2Request();
            if (userAuthentication instanceof CustomizeUsernamePasswordAuthenticationToken) {
                CustomizeUsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = (CustomizeUsernamePasswordAuthenticationToken) userAuthentication;
                this.permissions = usernamePasswordAuthenticationToken.getPermissions();
                this.user = usernamePasswordAuthenticationToken.getPrincipal();
            }
        }
    }


    /**
     * 具有则通过
     *
     * @param code code
     * @return 结果
     */
    public final boolean hasPermission(Object code) {
        return hasAnyPermission(code);
    }

    /**
     * 不具有则通过
     *
     * @param code code
     * @return 结果
     */
    public final boolean notHasPermission(Object code) {
        return !hasAnyPermission(code);
    }

    /**
     * 具有则通过
     *
     * @param code 权限code
     * @return 结果
     */
    public final boolean hasAnyPermission(String[] code) {
        if(permissions.contains(ALL_PERMISSION)){
            return true;
        }
        return hasAny(permissions, code);
    }
    /**
     * 具有则通过
     *
     * @param objects 权限code
     * @return 结果
     */
    public final boolean hasAnyPermission(Object... objects) {
        if(permissions.contains(ALL_PERMISSION)){
            return true;
        }
        return hasAny(permissions, objects);
    }

    /**
     * 不具有则通过
     *
     * @param code code
     * @return 结果
     */
    public final boolean notHasAnyPermission(String[] code) {
        if(permissions.contains(ALL_PERMISSION)){
            return true;
        }
        return !hasAny(permissions, code);
    }

    /**
     * 具有则通过
     *
     * @param scope scope
     * @return 结果
     */
    public final boolean hasScope(Object scope) {
        return hasAny(oAuth2Request.getScope(), scope);
    }

    /**
     * 不具有则通过
     *
     * @param scope scope
     * @return 结果
     */
    public final boolean notHasScope(Object scope) {
        return !hasAny(oAuth2Request.getScope(), scope);
    }

    /**
     * 具有则通过
     *
     * @param scope scope
     * @return 结果
     */
    public final boolean hasAnyScope(Object... scope) {
        return hasAny(oAuth2Request.getScope(), scope);
    }
    /**
     * 具有则通过
     *
     * @param scope scope
     * @return 结果
     */
    public final boolean hasAnyScope(String[] scope) {
        return hasAny(oAuth2Request.getScope(), scope);
    }

    /**
     * 不具有则通过
     *
     * @param scope scope
     * @return 结果
     */
    public final boolean notHasAnyScope(Object... scope) {
        return !hasAny(oAuth2Request.getScope(), scope);
    }

    /**
     * 具有则通过
     *
     * @param clientId clientId
     * @return 结果
     */
    public final boolean hasClient(String clientId) {
        String client = oAuth2Request.getClientId();
        if (client != null && clientId != null) {
            return client.equals(clientId);
        }
        return false;
    }

    /**
     * 不具有则通过
     *
     * @param clientId clientId
     * @return 结果
     */
    public final boolean notHasClient(String clientId) {
        String client = oAuth2Request.getClientId();
        if (client != null && clientId != null) {
            return !client.equals(clientId);
        }
        return true;
    }

    /**
     * 具有则通过
     *
     * @param resource resource
     * @return 结果
     */
    public final boolean hasResource(Object resource) {
        return hasAnyResource(resource);
    }

    /**
     * 不具有则通过
     *
     * @param resource resource
     * @return 结果
     */
    public final boolean notHasResource(Object resource) {
        return !hasAnyResource(resource);
    }

    /**
     * 具有则通过
     *
     * @param resource resource
     * @return 结果
     */
    public final boolean hasAnyResource(Object... resource) {
        return hasAny(oAuth2Request.getResourceIds(), resource);
    }
    /**
     * 具有则通过
     *
     * @param resource resource
     * @return 结果
     */
    public final boolean hasAnyResource(String [] resource) {
        return hasAny(oAuth2Request.getResourceIds(), resource);
    }

    /**
     * 不具有则通过
     *
     * @param resource resource
     * @return 结果
     */
    public final boolean notHasAnyResource(Object... resource) {
        return !hasAny(oAuth2Request.getResourceIds(), resource);
    }

    /**
     * 具有则通过
     *
     * @return 结果
     */
    private boolean hasAny(Collection<?> var1, Object... var2) {
         if (var1 != null) {
             for (Object o : var2) {
                 if (var1.contains(o)) {
                     return true;
                 }
             }
        }
        return false;
    }
    /**
     * 具有则通过
     *
     * @return 结果
     */
    private boolean hasAny(Collection<?> var1, String[] var2) {
        AtomicBoolean hasAny = new AtomicBoolean(false);
         if (var1 != null) {
             for (String code : var2) {
                 if (var1.contains(code)) {
                     hasAny.set(true);
                     break;
                 }
             }
         }
        return hasAny.get();
    }

    /**
     * 具有则通过
     *
     * @return 结果
     */
    public final boolean read() {
        return hasScope(read);
    }

    /**
     * 具有则通过
     *
     * @return 结果
     */
    public final boolean write() {
        return hasScope(write);
    }

    /**
     * 具有则通过
     *
     * @return 结果
     */
    public final boolean create() {
        return hasScope(create);
    }

    /**
     * 具有则通过
     *
     * @return 结果
     */
    public final boolean delete() {
        return hasScope(delete);
    }

    /**
     * 具有则通过
     *
     * @return 结果
     */
    public final boolean all() {
        return hasScope(ALL);
    }

    @Override
    public Object getFilterObject() {
        return this.filterObject;
    }

    @Override
    public void setFilterObject(Object o) {
        this.filterObject = o;
    }

    @Override
    public Object getReturnObject() {
        return this.returnObject;
    }

    @Override
    public void setReturnObject(Object o) {
        this.returnObject = o;
    }

    @Override
    public Object getThis() {
        return this.target;
    }

    public void setThis(Object target) {
        this.target = target;
    }

    public Collection<? extends Serializable> getPermissions() {
        return permissions;
    }

    public void setPermissions(Collection<? extends Serializable> permissions) {
        this.permissions = permissions;
    }

    public Object getUser() {
        return user;
    }

    public void setUser(Object user) {
        this.user = user;
    }
}
